/**
 * 
 */
package com.gragra.evaluateNouns;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import com.gragra.data.Corpus;
import com.gragra.nounIdentification.ConvergenceAssessment;
import com.gragra.nounIdentification.ConvergenceByIterations;
import com.gragra.nounIdentification.IncreasingMaxWithMin;
import com.gragra.nounIdentification.LinearIncreasingMax;
import com.gragra.nounIdentification.MaxAssessment;
import com.gragra.nounIdentification.NounIdentifier;
import com.gragra.nounIdentification.Scorer;
import com.gragra.nounIdentification.TypesPerTokenInitialLogWeighted;
import com.gragra.nounIdentification.TypesPerTokenLogWeighted;
import com.gragra.nounIdentification.TypesPerTokenScorer;
import com.gragra.parseOptions.BooleanOption;
import com.gragra.parseOptions.CommandLineException;
import com.gragra.parseOptions.ListOfIntegerOption;
import com.gragra.parseOptions.ListOfStringsOption;
import com.gragra.parseOptions.Parser;
import com.gragra.sampling.sequentialStructure.ExtensionInformationValue;
import com.gragra.sampling.sequentialStructure.WordAndTagHolder;
import com.gragra.sampling.sequentialStructure.WordType;
import com.gragra.sequentiaDataGenerator.BaseGenerator;
import com.gragra.sequentiaDataGenerator.CaseLevelAssigningSegmenter;
import com.gragra.sequentiaDataGenerator.EmptySegmentation;
import com.gragra.sequentiaDataGenerator.PunctuationSplitOffSegmenter;
import com.gragra.sequentiaDataGenerator.Segmentation;
/**
 * This class is used to evaluate the performance of the noun detection algorithm on a given corpus.
 * The noun detection is first trained with the necessary method settings and then evaluated against
 * the corpus it was trained on by assuming that the tag for a word can be used to identify whether a tag is
 * a noun or not. In order to do evaluation, it constructs a new
 * corpus that has the appropriate type annotation and that could also be used by other applications. Then it
 * uses the build in evaluation functions of the BaseNounIdentifier
 * @author Christoph Teichmann
 * 20.04.2013
 */
public class EvaluateNounsWithMapping
{
	/**
	 * 
	 */
	private static final String frameSizeOption = "fS";
	/**
	 * 
	 */
	private static final String relevantTagsOptions = "rT";
	/**
	 * @return the frameSize
	 */
	public int getFrameSize()
	{return this.frameSize;}
	/**
	 * 
	 */
	private static final String macroOption = "mA";
	/**
	 * @param microAverage the microAverage to set
	 */
	public void setMicroAverage(boolean microAverage)
	{this.microAverage = microAverage;}
	/**
	 * @param postMarker the postMarker to set
	 */
	public void setPostMarker(int postMarker)
	{this.postMarker = postMarker;}
	/**
	 * @param preMarker the preMarker to set
	 */
	public void setPreMarker(int preMarker)
	{this.preMarker = preMarker;}
	/**
	 * @param ma the ma to set
	 */
	public void setMaxMassessment(MaxAssessment ma)
	{this.setMa(ma);}
	/**
	 * @param score the score to set
	 */
	public void setScore(Scorer score)
	{this.score = score;}
	/**
	 * @param ca the ca to set
	 */
	public void setCa(ConvergenceAssessment ca)
	{this.ca = ca;}
	/**
	 * 
	 */
	private boolean microAverage = true;
	/**
	 * 
	 */
	private int postMarker = 0;
	/**
	 * 
	 */
	private int preMarker = 0;
	/**
	 * 
	 */
	private int frameSize = 3;
	/**
	 * 
	 */
	private MaxAssessment ma = new IncreasingMaxWithMin(new LinearIncreasingMax(1, 100), 1, 100);
	/**
	 * 
	 */
	private Scorer score = new TypesPerTokenScorer();
	/**
	 * 
	 */
	private ConvergenceAssessment ca = new ConvergenceByIterations(1);
	/**
	 * 
	 */
	private boolean toLowerCase = true;
	/**
	 * @return the toLowerCase
	 */
	public boolean isToLowerCase()
	{return this.toLowerCase;}
	/**
	 * @param toLowerCase the toLowerCase to set
	 */
	public boolean setToLowerCase(boolean toLowerCase)
	{return this.toLowerCase = toLowerCase;}
	/**
	 * 
	 */
	private String punctutationTag = null;
	/**
	 * @return the punctutationTag
	 */
	public String getPunctutationTag()
	{return this.punctutationTag;}
	/**
	 * @param punctutationTag the punctutationTag to set
	 * @return 
	 */
	public String setPunctutationTag(String punctutationTag)
	{return this.punctutationTag = punctutationTag;}
	/**
	 * 
	 * @param relevantTags
	 * @param relevantClass
	 * @return
	 * @throws IOException 
	 */
	public double[] evaluate(Parser cl,BufferedReader input) throws IOException
	{
		Object2ObjectOpenHashMap<String, ExtensionInformationValue> map = new Object2ObjectOpenHashMap<String, ExtensionInformationValue>();
		ListOfStringsOption lof = (ListOfStringsOption) cl.getInformation(relevantTagsOptions);
		String[] relevant = lof.getData();
		for(String s : relevant)
		{map.put(s,this.getRelevantClass());}
		setOptions(cl);
		Segmentation seg = this.getPunctutationTag() != null ? new PunctuationSplitOffSegmenter(punctutationTag) : new EmptySegmentation();
		seg = this.isToLowerCase() ? new CaseLevelAssigningSegmenter(seg) : seg;
		seg = new ExtendingSegmentation(seg, map);
		BaseGenerator bg = new BaseGenerator(seg);
		Corpus<WordAndTagHolder> corp = bg.createCorpus(input);
		NounIdentifier ni = createIdentifier(corp);
		ni.identify();
		return ni.evaluateOnSelf(microAverage);
	}
	/**
	 * 
	 * @param cl
	 * @param input
	 * @param outPut
	 * @throws IOException
	 */
	public void writeWords(Parser cl, BufferedReader input, BufferedWriter outPut) throws IOException
	{
		NounIdentifier ni = mainSetting(cl, input);
		ni.writeOutWords(outPut);
	}
	/**
	 * 
	 * @param cl
	 * @param input
	 * @param output
	 * @throws IOException
	 */
	public void annotateCorpusFrames(Parser cl,BufferedReader input, BufferedWriter output) throws IOException
	{
		NounIdentifier ni = mainSetting(cl, input);
		ni.annotateSelfFrames();
		ni.writeOutData(output);
	}
	/**
	 * 
	 * @param cl
	 * @param input
	 * @param outPut
	 * @throws IOException
	 */
	public void writeFrames(Parser cl, BufferedReader input, BufferedWriter outPut) throws IOException
	{
		NounIdentifier ni = mainSetting(cl, input);
		ni.writeOutFrames(outPut);
	}
	/**
	 * 
	 * @param cl
	 * @param input
	 * @return
	 * @throws IOException
	 */
	private NounIdentifier mainSetting(Parser cl, BufferedReader input)
			throws IOException
	{
		setOptions(cl);
		Segmentation seg = this.getPunctutationTag() != null ? new PunctuationSplitOffSegmenter(punctutationTag) : new EmptySegmentation();
		seg = this.isToLowerCase() ? new CaseLevelAssigningSegmenter(seg) : seg;
		BaseGenerator bg = new BaseGenerator(seg);
		Corpus<WordAndTagHolder> corp = bg.createCorpus(input);
		NounIdentifier ni = createIdentifier(corp);
		ni.identify();
		return ni;
	}
	/**
	 *
	 * @param corp
	 * @return
	 */
	protected NounIdentifier createIdentifier(Corpus<WordAndTagHolder> corp)
	{return new NounIdentifier(corp, frameSize, preMarker, postMarker, this.getScore(), getCa(), getMa());}
	/**
	 * 
	 * @param cl
	 */
	protected void setOptions(Parser cl)
	{
		ListOfIntegerOption loi = (ListOfIntegerOption) cl.getInformation(frameSizeOption);
		if(loi.hasBeenSet())
		{this.frameSize = loi.getSettings()[0];}
		BooleanOption bo = (BooleanOption) cl.getInformation(macroOption);
		if(bo.hasBeenSet())
		{this.microAverage = false;}
		loi = (ListOfIntegerOption) cl.getInformation(preMarkerOption);
		if(loi.hasBeenSet())
		{this.preMarker = (loi.getSettings()[0]);}
		loi = (ListOfIntegerOption) cl.getInformation(postMarkerOption);
		if(loi.hasBeenSet())
		{this.postMarker = (loi.getSettings()[0]);}
		int minWords = 200;
		int minFrames = 1;
		int frameGrow = 1;
		int wordGrow = 100;
		loi = (ListOfIntegerOption) cl.getInformation(increaseFramesOption);
		if(loi.hasBeenSet())
		{frameGrow = loi.getSettings()[0];}
		loi = (ListOfIntegerOption) cl.getInformation(increaseWordsOption);
		if(loi.hasBeenSet())
		{wordGrow = loi.getSettings()[0];}
		loi = (ListOfIntegerOption) cl.getInformation(minFramesOption);
		if(loi.hasBeenSet())
		{minFrames = loi.getSettings()[0];}
		loi = (ListOfIntegerOption) cl.getInformation(minWordsOption);
		if(loi.hasBeenSet())
		{minWords = loi.getSettings()[0];}
		this.setMa(new IncreasingMaxWithMin(new LinearIncreasingMax(frameGrow, wordGrow), minFrames, minWords));
		ListOfStringsOption los = (ListOfStringsOption) cl.getInformation(scorerOption);
		if(los.hasBeenSet())
		{
			String s = los.getData()[0];
			if(s.equals("TypesPerToken"))
			{this.setScore(new TypesPerTokenScorer());}
			if(s.equals("timesLog"))
			{this.setScore(new TypesPerTokenLogWeighted());}
			if(s.equals("initialLog"))
			{this.setScore(new TypesPerTokenInitialLogWeighted());}
		}
		loi = (ListOfIntegerOption) cl.getInformation(convergenceOption);
		if(loi.hasBeenSet())
		{this.setCa(new ConvergenceByIterations(loi.getSettings()[0]));}
		this.setToLowerCase(true);
		bo = (BooleanOption) cl.getInformation(toLowerOption);
		if(bo.hasBeenSet())
		{this.setToLowerCase(false);}
		this.setPunctutationTag(null);
		bo = (BooleanOption) cl.getInformation(punctOffOption);
		if(bo.hasBeenSet())
		{this.setPunctutationTag("PUNCT");}
	}
	/**
	 * @return the postMarker
	 */
	public int getPostMarker()
	{return this.postMarker;}
	/**
	 * @return the preMarker
	 */
	public int getPreMarker()
	{return this.preMarker;}
	/**
	 * @return the microAverage
	 */
	public boolean isMicroAverage()
	{return this.microAverage;}
	/**
	 * 
	 * @return
	 */
	protected ExtensionInformationValue getRelevantClass()
	{return WordType.Noun;}
	/**
	 * @throws CommandLineException 
	 * 
	 */
	public void addOptions(Parser to) throws CommandLineException
	{
		to.addOption(relevantTagsOptions, new ListOfStringsOption("this option takes the list of all the tags" +
											" that will identify the relevant class"));
		to.setOptionNecessary(relevantTagsOptions);
		to.addOption(frameSizeOption, new ListOfIntegerOption("this options takes the size of the frame that will " +
																				" be used"));
		to.addOption(preMarkerOption, new ListOfIntegerOption("this option can be used to set the amout of" +
																" padding markers that are added in front of the sentences," +
																" default is 0"));
		to.addOption(macroOption, new BooleanOption("set this option to true to enable macro-averaging for the evaluation"));
		to.addOption(postMarkerOption, new ListOfIntegerOption("this option can be used to set the amount of" +
											" post sentence padding that is used, default is 0"));
		to.addOption(increaseFramesOption, new ListOfIntegerOption("this sets the number of frames that will be" +
																		" added for each iteration"));
		to.addOption(increaseWordsOption, new ListOfIntegerOption("this sets the number of words that will be" +
																		" added for each iteration"));
		to.addOption(minFramesOption, new ListOfIntegerOption("the minimum number of frames that will be used"));
		to.addOption(minWordsOption, new ListOfIntegerOption("the minimum words that will be used"));
		to.addOption(scorerOption, new ListOfStringsOption("this option can be used to chose one of the" +
															" following types of scoring: TypesPerToken, timesLog, initialLog"));
		to.addOption(convergenceOption, new ListOfIntegerOption("the number of iterations that will be used"));
		to.addOption(toLowerOption, new BooleanOption("if this option is set then strings will not be transferred to lower case"));
		to.addOption(punctOffOption, new BooleanOption("if this option is set then punctuation will be split off from words"));
	}
	/**
	 * @throws CommandLineException 
	 * 
	 */
	public void addNoRelevantOptions(Parser to) throws CommandLineException
	{
		to.addOption(frameSizeOption, new ListOfIntegerOption("this options takes the size of the frame that will " +
																				" be used"));
		to.addOption(preMarkerOption, new ListOfIntegerOption("this option can be used to set the amout of" +
																" padding markers that are added in front of the sentences," +
																" default is 0"));
		to.addOption(macroOption, new BooleanOption("set this option to true to enable macro-averaging for the evaluation"));
		to.addOption(postMarkerOption, new ListOfIntegerOption("this option can be used to set the amount of" +
											" post sentence padding that is used, default is 0"));
		to.addOption(increaseFramesOption, new ListOfIntegerOption("this sets the number of frames that will be" +
																		" added for each iteration"));
		to.addOption(increaseWordsOption, new ListOfIntegerOption("this sets the number of words that will be" +
																		" added for each iteration"));
		to.addOption(minFramesOption, new ListOfIntegerOption("the minimum number of frames that will be used"));
		to.addOption(minWordsOption, new ListOfIntegerOption("the minimum words that will be used"));
		to.addOption(scorerOption, new ListOfStringsOption("this option can be used to chose one of the" +
															" following types of scoring: TypesPerToken, timesLog, initialLog"));
		to.addOption(convergenceOption, new ListOfIntegerOption("the number of iterations that will be used"));
		to.addOption(toLowerOption, new BooleanOption("if this option is set then strings will not be transferred to lower case"));
		to.addOption(punctOffOption, new BooleanOption("if this option is set then punctuation will be split off from words"));
	}
	/**
	 * @return the ma
	 */
	public MaxAssessment getMa()
	{return ma;}
	/**
	 * @param ma the ma to set
	 */
	public void setMa(MaxAssessment ma)
	{this.ma = ma;}
	/**
	 * @return the score
	 */
	public Scorer getScore()
	{return score;}
	/**
	 * @return the ca
	 */
	public ConvergenceAssessment getCa()
	{return ca;}
	/**
	 * 
	 */
	private final static String punctOffOption = "pO";
	/**
	 * 
	 */
	private final static String toLowerOption = "kU";
	/**
	 * 
	 */
	private final static String convergenceOption = "its";
	/**
	 * 
	 */
	private static final String scorerOption = "scO";
	/**
	 * 
	 */
	private static final String preMarkerOption = "preM";
	/**
	 * 
	 */
	private static final String postMarkerOption = "postM";
	/**
	 * 
	 */
	private static final String minFramesOption = "minF";
	/**
	 * 
	 */
	private static final String minWordsOption = "minW";
	/**
	 * 
	 */
	private static final String increaseWordsOption = "incW";
	/**
	 * 
	 */
	private static final String increaseFramesOption = "incF";
}